package server

import (
	"../../deps/lessgo/logger"
	"../../deps/lessgo/utilx"
	"../base"
	"../conf"
	"../net"
	"../state"
	"../store"
	"crypto/sha1"
	"fmt"
	"io"
	"strconv"
	"strings"
)

func MemberLeaderNew(req ActionRequst, addr string) {

	hostid := req.Get("ProposalContent")
	idxnum, _ := strconv.ParseInt(req.Get("ProposalVersion"), 10, 64)

	if hostid == "" || idxnum == 0 {
		return
	}

	leader := store.Cmd("GET", "sys:leader").String()
	if len(leader) > 4 {
		return
	}

	voteidx, _ := strconv.ParseInt(store.Cmd("GET", "tmp:voteidx").String(), 10, 64)
	voteval := store.Cmd("GET", "tmp:voteval").String()

	// ACCEPT!
	if voteidx == 0 || voteval == "" || voteidx == idxnum || (voteval == "" && voteidx <= idxnum) {

		store.Cmd("SET", "tmp:voteidx", idxnum)
		store.Cmd("SETEX", "tmp:voteval", 2, hostid)
	} else {
		return
	}

	ver, _ := store.Cmd("GET", "sys:keeper:ver").Int64()
	if ver < idxnum && hostid != conf.Config.HostId {
		store.Cmd("INCRBY", "sys:keeper:ver", (idxnum - ver)).Int64()
	}
	if idxnum < ver {
		idxnum = ver
	}

	msg := map[string]string{
		"action":        base.ActionLeaderAccept,
		"AcceptVersion": strconv.FormatInt(idxnum, 10),
		"AcceptContent": hostid,
	}
	net.PeerSend(hostid, msg)
}

func MemberLeaderAccept(req ActionRequst, addr string) {

	aptver, _ := strconv.ParseInt(req.Get("AcceptVersion"), 10, 64)
	hostid := req.Get("AcceptContent")

	if aptver == 0 || hostid == "" || hostid != conf.Config.HostId {
		return
	}

	lednum, _ := strconv.ParseInt(store.Cmd("GET", "tmp:peer:newleader").String(), 10, 64)
	if lednum != aptver {
		return
	}

	// If Valued
	valued, _ := store.Cmd("INCRBY", "tmp:peer:newleader:value", 1).Int()
	if 2*valued > len(state.Peers) {

		msg := map[string]string{
			"action":       base.ActionLeaderValue,
			"ValueVersion": req.Get("AcceptVersion"),
			"ValueContent": conf.Config.HostId,
		}
		net.PeersSend(msg)

		logger.Print("warn", "Peer."+hostid+" Leader Majory Valued")
	}
}

func MemberLeaderValue(req ActionRequst, addr string) {

	hostid := req.Get("ValueContent")
	if hostid == "" {
		return
	}

	store.Cmd("SETEX", "sys:leader", 12, hostid)
	store.Cmd("DEL", "tmp:voteidx", "tmp:voteval")
}

func MemberLeaderCast(req ActionRequst, addr string) {

	leader := req.Get("leader")
	ledVer, _ := strconv.ParseInt(req.Get("version"), 10, 64)
	peersRaw := req.Get("peers")

	if !utilx.ArrayContain(leader, state.PeerIds) {
		return
	}

	locLeader := store.Cmd("GET", "sys:leader").String()
	if locLeader != "" && locLeader != leader {
		return
	}
	store.Cmd("SETEX", "sys:leader", 12, leader)

	///
	locVer, _ := store.Cmd("GET", "sys:keeper:ver").Int64()
	if locVer < ledVer && leader != conf.Config.HostId {
		store.Cmd("INCRBY", "sys:keeper:ver", ledVer-locVer)
	}

	//
	if leader != conf.Config.HostId {
		store.Cmd("SETEX", "sys:keeper:started", 30, req.Get("started"))
	}

	//
	h := sha1.New()
	io.WriteString(h, peersRaw)
	peersDiff := fmt.Sprintf("%x", h.Sum(nil))

	if peersDiff == state.PeersDiff {
		return
	}

	logger.Print("warn", "Peers Refreshed ", peersRaw)

	peerids := []string{}
	asyncPeers := []map[string]string{}
	asyncDels := []string{}

	ps := strings.Split(peersRaw, ";")
	state.PeerLocker.Lock()
	for _, v := range ps {

		// format v1
		// 	0 version
		// 	1 hostid
		//	2 ip address
		// 	3 port
		// 	4 Protocal
		p := strings.Split(v, ",")
		if len(p) < 5 {
			continue
		}

		peerid := strings.ToLower(p[1])

		if _, ok := state.Peers[peerid]; !ok {

			state.Peers[peerid] = &state.Peer{
				Peer: base.Peer{
					Id: peerid,
				},
			}
		}
		state.Peers[peerid].Addr = p[2]
		state.Peers[peerid].Port = p[3]
		state.Peers[peerid].Proto = p[4]
		state.Peers[peerid].Url = fmt.Sprintf("%s://%s:%s", p[4], p[2], p[3])

		asyncPeers = append(asyncPeers, map[string]string{
			"hostid": peerid,
			"addr":   p[2],
			"port":   p[3],
			"proto":  p[4],
			"status": "1",
		})

		peerids = append(peerids, peerid)
	}

	if !utilx.ArrayEqual(peerids, state.PeerIds) {

		for _, id := range state.PeerIds {

			if !utilx.ArrayContain(id, peerids) {
				delete(state.Peers, id)
				asyncDels = append(asyncDels, id)
			}
		}

		state.PeerIds = peerids
	}

	state.PeersDiff = peersDiff

	state.PeerLocker.Unlock()

	//
	for _, v := range asyncPeers {
		store.Cmd("HMSET", "sys:peers:"+v["hostid"], v)
	}

	for _, v := range asyncDels {
		store.Cmd("DEL", "sys:peers:"+v)
	}
}
